package com.neusoft.hifly.push.email.utils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Random;

import javax.mail.Address;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Flags.Flag;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.ParseException;

import org.apache.commons.lang3.StringUtils;

import com.neusoft.hifly.commons.exception.SystemErrorException;
import com.neusoft.hifly.commons.id.IdWorker;
import com.neusoft.hifly.core.log.Log;
import com.neusoft.hifly.push.email.auth.MyAuthenticator;
import com.neusoft.hifly.push.email.pojo.MailFileObj;
import com.neusoft.hifly.push.email.pojo.MailReceiverInfo;

/**
 * Title: 核心类库
 * <p>
 * Description: 邮件接收器
 * <p>
 * Copyright: 2019
 * <p>
 * Company: 东软汉枫
 *
 * @author lh_neu
 * @version 1.0.0
 */
public class MailReceiver {

	/**
	 * 接收邮件
	 * <p>
	 * 收件后的信息还以MailReceiverInfo实体返回
	 *
	 * @param mailInfo
	 *            待发送的邮件信息
	 * @return 收到的邮件
	 * @exception SystemErrorException
	 *                自定义异常
	 */
	@SuppressWarnings("resource")
	public static List<MailReceiverInfo> receiveMail(final MailReceiverInfo mailInfo) throws SystemErrorException {
		Folder folder = null;
		Store store = null;
		final List<MailReceiverInfo> result = new ArrayList<MailReceiverInfo>(0);
		try {

			// Get a Properties object
			final Properties props = System.getProperties();
			props.setProperty("mail.pop3.host", mailInfo.getHost());
			props.setProperty("mail.smtp.auth", "true");

			// Get a Session object
			Session session = null;

			// 判断是否需要身份认证
			if (mailInfo.isValidate()) {
				//如果需要身份认证，则创建一个密码验证器
				session = Session.getDefaultInstance(props,
						new MyAuthenticator(mailInfo.getUserName(), mailInfo.getPassword()));
			} else {
				session = Session.getInstance(props, null);
			}

			session.setDebug(mailInfo.isDebug());

			// Get a Store object
			if (StringUtils.isNotEmpty(mailInfo.getStore())) {
				store = session.getStore(mailInfo.getStore());
			} else {
				store = session.getStore();
			}

			// Connect
			if (StringUtils.isNotEmpty(mailInfo.getHost()) || StringUtils.isNotEmpty(mailInfo.getUserName())
					|| StringUtils.isNotEmpty(mailInfo.getPassword())) {
				if (mailInfo.getPort() > 0) {
					store.connect(mailInfo.getHost(), mailInfo.getPort(), mailInfo.getUserName(),
							mailInfo.getPassword());
				} else {
					store.connect(mailInfo.getHost(), mailInfo.getUserName(), mailInfo.getPassword());
				}
			} else {
				store.connect();
			}

			// Open the Folder
			folder = store.getDefaultFolder();
			if (folder == null) {
				throw new SystemErrorException("No default folder");
			}

			folder = folder.getFolder(mailInfo.getFolder());
			if (folder == null) {
				throw new SystemErrorException("Invalid folder");
			}

			// try to open read/write and if that fails try read-only
			try {
				folder.open(Folder.READ_WRITE);
			} catch (final MessagingException ex) {
				folder.open(Folder.READ_ONLY);
			}
			final int totalMessages = folder.getMessageCount();

			if (totalMessages == 0) {
				folder.close(false);
				store.close();
				throw new SystemErrorException("Empty folder");
			}

			// Attributes & Flags for all messages ..
			final Message[] msgs = folder.getMessages();

			// Use a suitable FetchProfile
			final FetchProfile fp = new FetchProfile();
			fp.add(FetchProfile.Item.ENVELOPE);
			fp.add(FetchProfile.Item.FLAGS);
			fp.add("X-Mailer");
			folder.fetch(msgs, fp);

			if ((msgs != null) && (msgs.length >= 1)) {

				MailReceiverInfo obj = null;
				for (final Message message : msgs) {
					if (message == null) {
						continue;
					}
					obj = mailInfo.clone();

					if (obj == null) {
						continue;
					}
					// 获取附件
					MailReceiver.dumpEnvelope(obj, message);

					// 获取邮件内容
					obj.setContent(MailReceiver.getBody(obj, folder.getMessage(message.getMessageNumber())));

					//						// dumpPart(message);
					//						pr(obj.isDebug(), "Getting message number: " + message.getMessageNumber());
					//						Message m = null;
					//
					//						m = folder.getMessage(message.getMessageNumber());
					//						dumpPart(obj, m);
					result.add(obj);
				}

			}
		} catch (final Exception e) {
			throw new SystemErrorException("Oops, got exception! " + e.getMessage());
		} finally {
			try {
				if ((folder != null) && folder.isOpen()) {
					folder.close(false);
					folder = null;
				}
				if ((store != null) && store.isConnected()) {
					store.close();
					store = null;
				}
			} catch (final Exception e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * 读取收件信息
	 *
	 * @param obj
	 *            收件实体
	 * @param m
	 *            收取到的邮件
	 * @throws Exception
	 *             异常
	 */
	private static void dumpEnvelope(final MailReceiverInfo obj, final Message m) throws Exception {
		MailReceiver.pr(obj.isDebug(), "This is the message envelope");
		MailReceiver.pr(obj.isDebug(), "---------------------------");
		Address[] a;
		// FROM
		if ((a = m.getFrom()) != null) {
			for (final Address element : a) {
				MailReceiver.pr(obj.isDebug(), "FROM: " + element.toString());
				obj.setFromAddress(StringUtils.trimToEmpty(obj.getFromAddress()) + element.toString() + ";");
			}
		}

		// REPLY TO
		if ((a = m.getReplyTo()) != null) {
			for (final Address element : a) {
				MailReceiver.pr(obj.isDebug(), "REPLY TO: " + element.toString());
				obj.setReplyToAddress(StringUtils.trimToEmpty(obj.getReplyToAddress()) + element.toString() + ";");
			}
		}

		// TO
		if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
			for (final Address element : a) {
				MailReceiver.pr(obj.isDebug(), "TO: " + element.toString());

				obj.setToAddress(StringUtils.trimToEmpty(obj.getToAddress()) + element.toString() + ";");

				final InternetAddress ia = (InternetAddress) element;
				if (ia.isGroup()) {
					final InternetAddress[] aa = ia.getGroup(false);
					for (final InternetAddress element2 : aa) {
						MailReceiver.pr(obj.isDebug(), "  GROUP: " + element2.toString());
						obj.setToGroupAddress(
								StringUtils.trimToEmpty(obj.getToGroupAddress()) + element2.toString() + ";");
					}
				}
			}
		}

		// SUBJECT
		MailReceiver.pr(obj.isDebug(), "SUBJECT: " + m.getSubject());
		obj.setSubject(StringUtils.trimToEmpty(m.getSubject()));

		// DATE
		Date d = m.getSentDate();
		MailReceiver.pr(obj.isDebug(), "SendDate: " + (d != null ? d.toString() : "UNKNOWN"));
		obj.setSentDate(d);

		d = m.getReceivedDate();
		MailReceiver.pr(obj.isDebug(), "ReceivedDate: " + (d != null ? d.toString() : "UNKNOWN"));
		obj.setReceivedDate(d);

		// FLAGS
		final Flags flags = m.getFlags();
		final StringBuffer sb = new StringBuffer();
		final Flags.Flag[] sf = flags.getSystemFlags(); // get the system flags

		boolean first = true;
		for (final Flag f : sf) {
			String s;
			if (f == Flags.Flag.ANSWERED) {
				s = "\\Answered";
			} else if (f == Flags.Flag.DELETED) {
				s = "\\Deleted";
			} else if (f == Flags.Flag.DRAFT) {
				s = "\\Draft";
			} else if (f == Flags.Flag.FLAGGED) {
				s = "\\Flagged";
			} else if (f == Flags.Flag.RECENT) {
				s = "\\Recent";
			} else if (f == Flags.Flag.SEEN) {
				s = "\\Seen";
			} else {
				continue; // skip it
			}
			if (first) {
				first = false;
			} else {
				sb.append(' ');
			}
			sb.append(s);
		}

		final String[] uf = flags.getUserFlags(); // get the user flag strings
		for (final String element : uf) {
			if (first) {
				first = false;
			} else {
				sb.append(' ');
			}
			sb.append(element);
		}
		MailReceiver.pr(obj.isDebug(), "FLAGS: " + sb.toString());

		// X-MAILER
		final String[] hdrs = m.getHeader("X-Mailer");
		if (hdrs != null) {
			MailReceiver.pr(obj.isDebug(), "X-Mailer: " + hdrs[0]);
		} else {
			MailReceiver.pr(obj.isDebug(), "X-Mailer NOT available");
		}
	}

	/**
	 * Print a, possibly indented, string.
	 *
	 * @param isDebug
	 *            是否开启Debug调试
	 * @param s
	 *            输出的内容
	 */
	private static void pr(final boolean isDebug, final String s) {
		if (isDebug) {
			Log.error(s);
		}
	}

	/**
	 * 获取正文
	 *
	 * @param obj
	 *            收件实体
	 * @param message
	 *            消息
	 * @return Message
	 * @throws Exception
	 *             异常
	 */
	private static String getBody(final MailReceiverInfo obj, final Message message) throws Exception {
		String body = null;
		try {
			if (message.getContent() instanceof Multipart) {
				final Multipart multipart = (Multipart) message.getContent();
				Part part = null;
				final int count = multipart.getCount();
				// 依次处理各个部分
				for (int j = 0, n = count; j < n; j++) {
					part = multipart.getBodyPart(j);
					// 解包, 取出 MultiPart的各个部分, 每部分可能是邮件内容, 也可能是另一个小包裹(MultipPart)
					// 判断此包裹内容是不是一个小包裹, 一般这一部分是 正文 Content-Type:
					// multipart/alternative

					if (part.getContent() instanceof String) {
						final String disp = part.getDisposition();
						if (disp == null) {
							// 文本内容
							body = (String) part.getContent();
						} else {
							// 附件信息

							// many mailers don't include a Content-Disposition
							if ((disp == null) || disp.equalsIgnoreCase(Part.ATTACHMENT)) {
								String filename = part.getFileName();
								if (StringUtils.isNotEmpty(filename)) {
									filename = MimeUtility.decodeText(filename);
									MailReceiver.pr(obj.isDebug(), "Saving attachment to file " + filename);
									// 下载邮件写到服务器
									final MailFileObj file = new MailFileObj();
									final IdWorker idWorker = new IdWorker(new Random().nextInt(30) + 1, 1);
									file.setId(idWorker.nextId());
									file.setName(filename);
									file.setIs(part.getInputStream());
									List<MailFileObj> files = obj.getAttachs();
									if (files == null) {
										files = new ArrayList<MailFileObj>(0);
									}
									files.add(file);
									obj.setAttachs(files);
								}
								MailReceiver.pr(obj.isDebug(), "---------------------------");
							}
						}

					} else if (part.getContent() instanceof Multipart) {
						body = MailReceiver.getMailContent(obj, part);
						return body;
					}
				}
				part = null;
			} else if (message.getContent() instanceof String) {
				body = (String) message.getContent();
			}
		} catch (final Exception e) {
			e.printStackTrace();
			body = "";
		}

		return body;
	}

	/**
	 * 获取邮件内容
	 *
	 * @param obj
	 *            收件实体
	 * @param part
	 *            内容
	 * @return null
	 * @throws Exception
	 *             异常
	 */
	private static String getMailContent(final MailReceiverInfo obj, final Part part) throws Exception {
		final String contenttype = part.getContentType();
		final int nameindex = contenttype.indexOf("name");
		final StringBuffer bodytext = new StringBuffer();
		boolean conname = false;
		if (nameindex != -1) {
			conname = true;
		}

		final String ct = part.getContentType();
		try {
			MailReceiver.pr(obj.isDebug(), "CONTENT-TYPE: " + (new ContentType(ct)).toString());
		} catch (final ParseException pex) {
			MailReceiver.pr(obj.isDebug(), "BAD CONTENT-TYPE: " + ct);
		}

		if (part.isMimeType("text/plain") && !conname) {
			bodytext.append((String) part.getContent());
		} else if (part.isMimeType("text/html") && !conname) {
			bodytext.append((String) part.getContent());
		} else if (part.isMimeType("multipart/*")) {
			final Multipart multipart = (Multipart) part.getContent();
			final int counts = multipart.getCount();
			for (final int i = 0; i < counts;) {
				return MailReceiver.getMailContent(obj, multipart.getBodyPart(i));
			}
		} else if (part.isMimeType("message/rfc822")) {
			return MailReceiver.getMailContent(obj, (Part) part.getContent());
		}
		return bodytext.toString();
	}

}
